/*
 * IntrospectionClient.java
 *
 * Created on May 13, 2007, 3:24 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.atomojo.app.client;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.logging.Logger;
import org.infoset.xml.Characters;
import org.infoset.xml.Element;
import org.infoset.xml.Item;
import org.infoset.xml.ItemDestination;
import org.infoset.xml.Name;
import org.infoset.xml.Named;
import org.infoset.xml.XMLException;
import org.restlet.Client;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.data.ChallengeResponse;
import org.restlet.data.ChallengeScheme;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.Protocol;
import org.restlet.data.Reference;
import org.restlet.representation.Representation;

/**
 *
 * @author alex
 */
public class IntrospectionClient extends AppClient
{
   
   static final MediaType ATOM_SERVICE_XML = MediaType.valueOf("application/atomsvc+xml");
   
   public interface ServiceListener {
      void onStartWorkspace(String title);
      void onCollection(EntryCollection collection);
      void onEndWorkspace();
   }
   
   Logger log;
   Reference location;
   URI ulocation;
   
   class ServiceDestination implements ItemDestination {
      ServiceListener listener;
      int state;
      boolean startedWorkspace;
      String title;
      URI collectionLocation;
      ItemDestination delegate;
      StringBuilder buffer;
      ServiceDestination(ServiceListener listener)
      {
         this.listener = listener;
         this.state = 0;
         this.buffer = null;
      }
      public void send(Item item)
         throws XMLException
      {
         switch (state) {
            case 0:
               if (item.getType()==Item.ItemType.ElementItem) {
                  Name name = ((Named)item).getName();
                  if (name.equals(WORKSPACE) || name.equals(OLD_WORKSPACE)) {
                     state = 1;
                     startedWorkspace = false;
                  }
               }
               break;
            case 1:
               switch (item.getType()) {
                  case ElementItem:
                  {
                     Element e = (Element)item;
                     Name name = ((Named)item).getName();
                     if (name.equals(COLLECTION) || name.equals(OLD_COLLECTION)) {
                        if (!startedWorkspace) {
                           listener.onStartWorkspace("");
                           startedWorkspace = true;
                        }
                        String href = e.getAttributeValue("href");
                        if (href==null) {
                           return;
                        }
                        title = null;
                        URI base = e.getBaseURI();
                        if (base==null) {
                           base = ulocation;
                        }
                        collectionLocation = base.resolve(href);
                        //Logger.getAnonymousLogger().info("Resolved '"+href+"' against '"+base+"' to get '"+collectionLocation+"'");
                        state = 2;
                     } else if (name.equals(TITLE_NAME)) {
                        buffer = new StringBuilder();
                        title = "";
                     }
                  }
                     break;
                  case ElementEndItem:
                  {
                     Name name = ((Named)item).getName();
                     if (name.equals(WORKSPACE) || name.equals(OLD_WORKSPACE)) {
                        listener.onEndWorkspace();
                        state = 0;
                     } else if (name.equals(TITLE_NAME)) {
                        listener.onStartWorkspace(buffer.toString());
                        startedWorkspace = true;
                        buffer = null;
                     }
                  }
                     break;
                  case CharactersItem:
                     if (buffer!=null) {
                        buffer.append(((Characters)item).getText());
                     }
                     
               }
               break;
            case 2:
               switch (item.getType()) {
                  case ElementItem:
                  {
                     Name name = ((Named)item).getName();
                     if (name.equals(TITLE_NAME)) {
                        buffer = new StringBuilder();
                     }
                  }
                     break;
                  case ElementEndItem:
                  {
                     Name name = ((Named)item).getName();
                     if (name.equals(COLLECTION) || name.equals(OLD_COLLECTION)) {
                        listener.onCollection(new EntryCollection(title,collectionLocation));
                        state = 1;
                     }
                  }
                     break;
                  case CharactersItem:
                     if (buffer!=null) {
                        buffer.append(((Characters)item).getText());
                     }
                     
               }
         }
      }
   }
   
   /** Creates a new instance of IntrospectionClient */
   public IntrospectionClient(Logger log,URI location)
   {
      this.log = log;
      this.ulocation = location;
      this.location = new Reference(location.toString());
   }
   
   /** Creates a new instance of IntrospectionClient */
   public IntrospectionClient(Logger log,Reference location)
      throws URISyntaxException
   {
      this.log = log;
      this.location = location;
      this.ulocation = new URI(location.toString());
   }
   
   public void introspect(ServiceListener listener) 
      throws IOException,XMLException
   {
      Client client = new Client(new Context(Logger.getLogger(IntrospectionClient.class.getName())),Protocol.valueOf(location.getScheme()));
      client.getContext().getAttributes().put("hostnameVerifier", org.apache.commons.ssl.HostnameVerifier.DEFAULT);
      Request request = new Request(Method.GET,location.toString());
      if (identity!=null) {
         request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,identity.getName(),identity.getPassword()));
      }
      if (cookie!=null) {
         request.getCookies().add(cookie);
      }
      Response response = client.handle(request);
      if (response.getStatus().isSuccess()) {
         Representation rep = response.getEntity();
         String mediaTypeName = rep.getMediaType().getName();
         if (!mediaTypeName.equals(ATOM_SERVICE_XML.getName())) {
            if (mediaTypeName.equals(MediaType.APPLICATION_XML.getName()) || mediaTypeName.equals(MediaType.TEXT_XML.getName()) || mediaTypeName.equals("application/atomserv+xml")) {
               log.info("Incorrect media type "+mediaTypeName+" received, recovering.");
            } else {
               throw new IOException("Invalid media type received: "+rep.getMediaType());
            }
         }
         XMLRepresentationParser parser = new XMLRepresentationParser();
         parser.parse(response,new ServiceDestination(listener));
      } else {
         throw new IOException("Introspection failed to get a service document, status="+response.getStatus().getCode());
      }
   }
   
}
